/* Copyright Florian Schweitzer
 * File created 04.12.2012
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */
package org.openuat.android.authentication.ambientaudio;

import java.io.IOException;

import org.openuat.android.Constants;
import org.openuat.android.authentication.AndroidAuthenticationSession.AUTHENTICATION_TYPE;
import org.openuat.android.channel.main.bluetooth.AndroidRFCOMMServer;
import org.openuat.android.channel.main.bluetooth.OnConnectionAcceptedListener;
import org.openuat.android.service.Client;
import org.openuat.android.service.OpenUATService;
import org.openuat.android.service.SecureChannel;
import org.openuat.android.service.connectiontype.IConnectionType;
import org.openuat.channel.main.HostAuthenticationServer;
import org.openuat.channel.main.RemoteConnection;

import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
import android.os.RemoteException;
import android.util.Log;

/**
 * Class for managing the ambient-audio-authentication method
 * @author Florian Schweitzer
 *
 */
public class AmbientAudioPairing implements OnConnectionAcceptedListener, OnAmbientAudioResultListener {
	
	/**
	 * The single AmbientAudioPairing instance
	 */
	private static AmbientAudioPairing instance = null;
	
	/**
	 * The used authentication server
	 */
	private static HostAuthenticationServer usedServer = null;
	
	/**
	 * The listener for the ambient audio result
	 */
	private static OnAmbientAudioResultListener ambientAudioResultListener = null;
	
	/**
	 * The ambient audio server object
	 */
	private static AmbientAudioServer ambientaudioServer = null;
	
	/**
	 * The ambient audio client object
	 */
	private static AmbientAudioClient ambientaudioClient = null;
	
	/**
	 * Gets a single instance of the Ambient Audio Pairing -object. Starts listening for
	 * incoming connections.
	 * 
	 * @return AmbientAudioPairing	The single AmbientAudioPairing instance
	 * @throws IOException
	 */
	public static AmbientAudioPairing getInstance(IConnectionType.CONNECTION_TYPE connType) throws IOException {
		if (AmbientAudioPairing.instance == null) {
			
			AmbientAudioPairing.instance = new AmbientAudioPairing();
			
			switch (connType) {
			case WIFI:
				usedServer = new TCPPortServerAmbientAudio(Constants.TCP_PORT, Constants.PROTOCOL_TIMEOUT,
						Constants.KEEP_CONNECTED, Constants.USE_JSSE,AUTHENTICATION_TYPE.AMBIENTAUDIO,
						AmbientAudioPairing.getInstance(connType));
				usedServer.start();
				break;
			case BLUETOOTH:
				usedServer = new AndroidRFCOMMServer(BluetoothAdapter.getDefaultAdapter(),
						"OpenUATService",Constants.PROTOCOL_TIMEOUT,
						Constants.KEEP_CONNECTED, Constants.USE_JSSE,AUTHENTICATION_TYPE.AMBIENTAUDIO,
						AmbientAudioPairing.getInstance(connType));
				usedServer.start();
				break;
			}
		}
		return AmbientAudioPairing.instance;
	}
	
	/**
	 * Stops the authentication server
	 */
	public static void stopServer() {
		try {
			closeAmbientAudio();
			
			if (usedServer != null) {
				if (usedServer instanceof AndroidRFCOMMServer) {
					AndroidRFCOMMServer server = (AndroidRFCOMMServer)usedServer;
					server.stop();
				}
				usedServer.stop();
				usedServer = null;
				Log.i("AmbientAudioPairing", "stopped authentication server");				
			}
			
			if (instance != null) {
				instance = null;
				ambientAudioResultListener = null;	
			}			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onConnectionAccepted(RemoteConnection remote) {
		ambientaudioServer = new AmbientAudioServer(remote,getAmbientAudioResultListenerInstance());
	}
	
	/**
	 * Creates the ambient audio client object
	 * @param remote	The remote connection
	 */
	public static void createAmbientAudioClient(RemoteConnection remote) {
		ambientaudioClient = new AmbientAudioClient(remote,getAmbientAudioResultListenerInstance());
	}
	
	/**
	 * Notify user that authentication started
	 */
	public static void notifyAuthenticationStart() {
		Notification.Builder mBuilder =
	        new Notification.Builder(OpenUATService.context)
	        .setSmallIcon(android.R.drawable.ic_dialog_info)
	        .setContentTitle("Authentication started...")
	        .setAutoCancel(true)
	        .setTicker("Authentication started ...")
	        .setContentText("This process usually takes about a minute");
		
		// Publish notification.
		OpenUATService.mNotificationManager.notify(
					Constants.NOTIF_AUTH_STARTED, mBuilder.getNotification());		
	}
	
	/**
	 * Stop the ambient audio server and the ambient audio client
	 */
	private static void closeAmbientAudio() {
		if (ambientaudioClient != null) {
			ambientaudioClient.finish();
			ambientaudioClient = null;
		}		
		
		if (ambientaudioServer != null) {
			ambientaudioServer.finish();
			ambientaudioServer = null;
		}
	}
	
	/**
	 * Listener for getting informed of the authentication result
	 * @return
	 */
	public static OnAmbientAudioResultListener getAmbientAudioResultListenerInstance() {
		if (ambientAudioResultListener == null) {
			ambientAudioResultListener = new AmbientAudioPairing();
		}
		return ambientAudioResultListener;
	}

	@Override
	public synchronized void onSessionKeyGeneratedSuccess(byte[] key, RemoteConnection remote) {
		Log.i(this.toString(),"entered onSessionKeyGeneratedSuccess");	
		
		OpenUATService.mNotificationManager.cancel(Constants.NOTIF_AUTH_STARTED);
		
		Notification.Builder mBuilder =
	        new Notification.Builder(OpenUATService.context)
	        .setSmallIcon(android.R.drawable.ic_dialog_info)
	        .setContentTitle("Authentication succeed!")
	        .setAutoCancel(true)
	        .setTicker("Authentication successful!")
	        .setContentText("Successfully authenticated with ambient audio!");
		
		// Publish notification.
		OpenUATService.mNotificationManager.notify(
					Constants.NOTIF_AUTH_SUCCESS, mBuilder.getNotification());
		
		//create SecureChannel
		try {
			if (key != null && remote != null) {
				SecureChannel secChannel = new SecureChannel(remote);
				secChannel.setSessionKey(key);
				Client client = IConnectionType.getClientByRemote(remote);
				
				if (client != null) {
					Log.i(this.toString(),"client != null, publishing secure channel");
					client.setSecureChannel(secChannel);					
				} else {
					Log.i(this.toString(),"client is null, no secure channel published");
				}
			}			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}				
	}

	@Override
	public synchronized void onSessionKeyGeneratedFailure(
			RemoteConnection remote, Exception e) {
		Log.e(this.toString(), "entered onSessionKeyGeneratedFailure", e);
		
		OpenUATService.mNotificationManager.cancel(Constants.NOTIF_AUTH_STARTED);		
		
		//Failure notification
		Notification.Builder mBuilder =
	        new Notification.Builder(OpenUATService.context)
	        .setSmallIcon(android.R.drawable.ic_dialog_alert)
	        .setContentTitle("Authentication failed!")
	        .setAutoCancel(true)
	        .setTicker("Authentication failed!")
	        .setContentText("Failure during authentication with ambient audio!");	
		
		// Publish notification.
		OpenUATService.mNotificationManager.notify(
					Constants.NOTIF_AUTH_FAILURE, mBuilder.getNotification());
	}
}
